/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "arkweb/chromium_ext/ui/touch_selection/touch_selection_controller_ext.h"

#include "arkweb/chromium_ext/ui/touch_selection/touch_selection_controller_utils.h"
#include "base/logging.h"

namespace ui {
TouchSelectionControllerExt::TouchSelectionControllerExt(
    TouchSelectionControllerClient* client,
    const Config& config)
    : TouchSelectionController(client, config) {}

TouchSelectionControllerExt::~TouchSelectionControllerExt() {}

#if BUILDFLAG(ARKWEB_MENU)
void TouchSelectionControllerExt::UpdateSelectionChanged(
    const TouchSelectionDraggable& draggable) {
  if (&draggable != insertion_handle_.get()) {
    client_->OnSelectionEvent(SELECTION_HANDLES_UPDATEMENU);
  }
}

// LCOV_EXCL_START
bool TouchSelectionControllerExt::IsLongPressDragSelectionActive() {
  return longpress_drag_selector_.IsActive();
}
// LCOV_EXCL_STOP

// LCOV_EXCL_START
void TouchSelectionControllerExt::ResetResponsePendingInputEvent() {
  response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
}
// LCOV_EXCL_STOP

// LCOV_EXCL_START
bool TouchSelectionControllerExt::SelectOverImg() {
  if (active_status_ == SELECTION_ACTIVE && start_selection_handle_ &&
      end_selection_handle_ && show_touch_handles_) {
    if (start_selection_handle_->IsActive() ||
        end_selection_handle_->IsActive()) {
      LOG(INFO) << "selection temporarily hide";
      utils_->SetResetSelectionTemporarily(true);
      return true;
    }
  }
  if (longpress_drag_selector_.IsDragging()) {
    LOG(INFO) << "long selection temporarily hide";
    utils_->SetResetSelectionTemporarily(true);
    return true;
  }
  return false;
}
// LCOV_EXCL_STOP

bool TouchSelectionControllerExt::OnHandleSwap(bool need_swap,
                                               const gfx::SelectionBound& start,
                                               const gfx::SelectionBound& end) {
  bool handled_swap = need_swap;
  if (!need_swap) {
    handled_swap = (end_ == end && end_selection_handle_->IsActive()) ||
                   (start_ == start && start_selection_handle_->IsActive());
  }
  return handled_swap;
}

bool TouchSelectionControllerExt::IsEndHandleNotVisible(
    const MotionEvent& event) {
  if (!end_selection_handle_->AsTouchHandleExt()->GetVisible() &&
      start_selection_handle_->AsTouchHandleExt()->GetVisible()) {
    LOG(INFO) << "Handle selection event, start is visible end is not visible.";
    return true;
  }
  return false;
}

void TouchSelectionControllerExt::ArkSelectBetweenCoordinates(
    const gfx::PointF& base,
    const gfx::PointF& extent) {
  if (!end_selection_handle_->AsTouchHandleExt()->GetVisible() &&
      end_.GetHeight() == 0 && start_.GetHeight() != 0) {
    LOG(INFO) << "The end handle information is empty.";
    client_->SelectBetweenCoordinatesV2(
        anchor_drag_to_selection_start_ ? extent : base, true);
  } else if (!start_selection_handle_->AsTouchHandleExt()->GetVisible() &&
             start_.GetHeight() == 0 && end_.GetHeight() != 0) {
    LOG(INFO) << "The start handle information is empty.";
    client_->SelectBetweenCoordinatesV2(
        anchor_drag_to_selection_start_ ? base : extent, false);
  } else {
    client_->SelectBetweenCoordinates(base, extent);
  }
}

void TouchSelectionControllerExt::ResetPositionAfterDragEnd(
    const TouchSelectionDraggable& draggable) {
  if (&draggable == start_selection_handle_.get()) {
    start_selection_handle_->AsTouchHandleExt()->ResetPositionAfterDragEnd();
  }
  if (&draggable == end_selection_handle_.get()) {
    end_selection_handle_->AsTouchHandleExt()->ResetPositionAfterDragEnd();
  }
}

void TouchSelectionControllerExt::OnInsertionChangedExt(const gfx::SelectionBound& start,
                                                        const gfx::SelectionBound& end) {
  start_ = start;
  end_ = end;
  DeactivateSelection();
  const bool activated = ActivateInsertionIfNecessary();
  if (insertion_handle_) {
    const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
    if (auto* handle_ext = insertion_handle_->AsTouchHandleExt()) {
      handle_ext->SetEdge(start_.edge_start(), start_.edge_end());
    }
    insertion_handle_->SetFocus(start_.edge_start(), start_.edge_end());
    insertion_handle_->SetVisible(GetStartVisible(), animation);
    if (insertion_handle_->AsTouchHandleExt()) {
      insertion_handle_->AsTouchHandleExt()->SetInsertHandleAlpha(0.f);
    }
    UpdateHandleLayoutIfNecessary();
  }
}

bool TouchSelectionController::IsShowHandle() {
  return client_ && client_->IsShowHandle();
}
#endif

#if BUILDFLAG(ARKWEB_AI)
int32_t TouchSelectionControllerExt::GetTouchNums(const MotionEvent& event) {
  PreTouchInfo curTouchInfo;
  curTouchInfo.x = event.GetX(0);
  curTouchInfo.y = event.GetY(0);
  curTouchInfo.start = event.GetEventTime();
  if (gestureTouchQueue_.empty()) {
    gestureTouchQueue_.push(curTouchInfo);
    return SINGLE_CLICK_NUM;
  }
  PreTouchInfo preTouchInfo = gestureTouchQueue_.back();
  bool continuous = IsContinuousEvent(preTouchInfo, curTouchInfo, true);
  if (continuous) {
    gestureTouchQueue_.push(curTouchInfo);
    if (gestureTouchQueue_.size() == TRIPLE_CLICK_NUM) {
      gestureTouchQueue_.pop();
      return TRIPLE_CLICK_NUM;

    }
    return DOUBLE_CLICK_NUM;
  }
  if (!gestureTouchQueue_.empty()) {
    std::queue<PreTouchInfo> empty;
    swap(empty, gestureTouchQueue_);
    gestureTouchQueue_.push(curTouchInfo);
  }
  return SINGLE_CLICK_NUM;
}

bool TouchSelectionControllerExt::IsContinuousEvent(const PreTouchInfo& first_down,
                                    const PreTouchInfo& second_down,
                                    bool should_process_double_tap) {
  const base::TimeDelta delta_time =
     second_down.start - first_down.start;
  if (delta_time > double_tap_timeout_) {
    return false;
  }
  const float delta_x = first_down.x - second_down.x;
  const float delta_y = first_down.y - second_down.y;
  return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_);
}

void TouchSelectionControllerExt::SetTouchNumsForHandle(const MotionEvent& event) {
  int32_t continuous_touch_nums = GetTouchNums(event);
  if (insertion_handle_) {
    if (insertion_handle_->AsTouchHandleExt()) {
      insertion_handle_->AsTouchHandleExt()->SetEdge(start_.edge_start(),
                                                     start_.edge_end());
    }
    insertion_handle_->SetTouchNums(continuous_touch_nums);
  }
  if(start_selection_handle_) {
    if (start_selection_handle_->AsTouchHandleExt()) {
      start_selection_handle_->AsTouchHandleExt()->SetEdge(start_.edge_start(),
                                                           start_.edge_end());
    }
    start_selection_handle_->SetTouchNums(continuous_touch_nums);
  }
  if(end_selection_handle_) {
    if (end_selection_handle_->AsTouchHandleExt()) {
      end_selection_handle_->AsTouchHandleExt()->SetEdge(end_.edge_start(),
                                                         end_.edge_end());
    }
    end_selection_handle_->SetTouchNums(continuous_touch_nums);
  }
}
#endif
}  // namespace ui
